/**
* @author Jacye.Ouyang
*/
function indicatorCalculation() {  
};
indicatorCalculation.prototype = {
    filtrateCalculatedData: function(stIndex,endIndex,isFilled,aData,ty,type,startpos) {
        if (aData == null || aData.length<1) {
            return {data: [],spos: null};
        }
        var data  = [];
		var lgg = aData[0].length, sposIndex = lgg>3?2:1;
        var st = aData[0][aData[0].length-1], end = aData[aData.length-1][aData[aData.length-1].length-1];
        var interVal = 1;
		if (ty == "i"){
			interVal = 600000;
		}else if (ty == "d") { 
            interVal = 1;
        } else if (ty == "w") {
            interVal = 7;
        } else if (ty == "m") {
            interVal = 30;
        }
        var inx = 0,spos = (typeof(startpos)=="undefined"?null:startpos);
        if (isFilled && stIndex < st) {   
            for (var i = stIndex; i < st; i += interVal) {
                 var dp = [inx,NaN,i];
                 data.push(dp);
                 inx++;
            }     
        }
        for (var j = 0; j < aData.length; j++) {
            if (aData[j][aData[j].length-1] < stIndex || aData[j][aData[j].length-1] > endIndex) {
                continue;
            }
			if(type=="return"&&spos == null && !isNaN(aData[j][sposIndex])){
				spos = aData[j][sposIndex];
			}
            var dpp = [];
            dpp.push(inx);
			if(type=="return" && spos !=null){    // convert to return value.
				dpp.push(100*(aData[j][1]-spos)/spos);
			}else{
				dpp.push(aData[j][1])
			}
            for (var h=2;h<aData[j].length;h++) {
                dpp.push(aData[j][h]);
            }
            data.push(dpp);
            inx++;
        }
        /*if (isFilled && endIndex > end) {   // not fill the end.
            for (var g = end; g<endIndex; g+=interVal) {
                 var d = [inx,NaN,g+interVal];
                 data.push(d);
                 inx++;
            }     
        }*/
        return {data: data,spos: spos};
    },
    EMA: function(aData,n) {
        if(typeof(aData)=="undefined"||aData.length==0||isNaN(n)){
			return null;
		}
        var aCache = [];
        var lg = aData.length, k  = 2/(n+1), iPreEma= 0, iSmv = 0,m=0;
        var needCut = true, st = 0;;
        for (var i = 0; i < lg; i++) {
            if (needCut && (aData[i] == null || isNaN(aData[i]))) {
                aCache.push(NaN);
                st = i + 1;
                continue;
            }
            needCut = false;
            if ((i - st) < n - 1) {
                aCache.push(NaN);
                if (aData[i] != null && !isNaN(aData[i])) {
                    iSmv += aData[i], m++;
                }
                continue;
            } else if ((i - st) == n - 1) {
                if (aData[i] != null && !isNaN(aData[i])) {
                    iSmv += aData[i], m++;
                }
                aCache.push((iSmv == 0 ? NaN : (iSmv / m)));
                if (!isNaN(aCache[i])) {
                    iPreEma = aCache[i];
                }
            } else {
                var iCurPrice = (aData[i] == null ? NaN : aData[i]);
                var iEma = iPreEma + (iCurPrice - iPreEma) * k;
                aCache.push(iEma);
                if (!isNaN(aCache[i])) {
                    iPreEma = iEma;
                }
            }
        }
        return aCache;
    },
    calculateSma:function(aData,ty,n) {
        if (n == null || n< 0 || aData == null || aData.length < n) {
            return null;
        }
        var aCache = [];
        var lg = aData.length, iSum = 0, m=0;
        for (var i = 0; i<lg; i++) {
            if (aData[i] != null && !isNaN(aData[i][1])) {
               iSum += aData[i][1];
               m++;
            }
            if (i < n-1) {
                aCache.push([i,NaN,aData[i][aData[i].length-1]]);
            } else { 
                var smv = isNaN(aData[i][1])||m<=0?NaN:(iSum/m); 
                aCache.push([i,smv,aData[i][aData[i].length-1]]);
                if (aData[i-n+1] != null && !isNaN(aData[i-n+1][1])) {
                    iSum-=aData[i-n+1][1];
                    m--;
                }
            }           
        }
        return aCache;
    },
    
    calculateEma: function(aData,ty,n) {
        if (n == null || n< 0 || aData == null || aData.length < n) {
           return null;
        }
        var aCache = [];
        var lg = aData.length, k  = 2/(n+1), iPreEma= 0, iSmv = 0,m=0;
        var needCut = true, st = 0;;
        for (var i=0;i<lg;i++) {
            if(needCut && (aData[i] == null || isNaN(aData[i][1]))) {   
                aCache.push([i,NaN,aData[i][aData[i].length-1]]);
                st = i+1;
                continue;
            }
            needCut = false; 
            if ((i-st) < n-1) { 
             aCache.push([i,NaN,aData[i][aData[i].length-1]]);
             if (aData[i] != null && !isNaN(aData[i][1])) {
                 iSmv+=aData[i][1], m++;
             }
             continue;
            } else if ((i-st) == n-1) { 
             if (aData[i] != null && !isNaN(aData[i][1])) {
                 iSmv+=aData[i][1], m++;
             }
             aCache.push([i,(iSmv == 0?NaN:(iSmv/m)),aData[i][aData[i].length-1]]);
             if (!isNaN(aCache[i][1]) ) {
                 iPreEma = aCache[i][1];
             }
            } else { 
             var iCurPrice = (aData[i] ==null? NaN:aData[i][1]); 
             var iEma = iPreEma + (iCurPrice-iPreEma)*k;
             aCache.push([i,iEma,aData[i][aData[i].length-1]]); 
             if (!isNaN(aCache[i][1])) { 
                 iPreEma = iEma;
             }
            }
        }
        return aCache;
    },
    
    calculateBBands:function(aData,ty,n,dev) { 
        if (n< 0 || aData == null || aData.length < n) {
           return null;
        } 
        var aCache = [];
        var lg = aData.length; aCache[0] = [], aCache[1] = [];
        var iSum = 0, m = 0;
        for (var i = 0; i < lg; i++) { 
            if (i < n-1) {
                aCache[0].push([i,NaN,NaN,aData[i][aData[i].length-1]]);
				aCache[1].push([i,NaN,NaN,aData[i][aData[i].length-1]]);
                if (aData[i] != null && !isNaN(aData[i][1])) {
                    iSum += aData[i][1],
                    m++;
                }
                continue;
            } else { 
                if (aData[i] != null && !isNaN(aData[i][1])) {
                    iSum+= aData[i][1];
                    m++;
                } 
                var iAvg = m>0?(iSum/m):NaN; var iStdDev = 0,iStdDevSum = 0,  iNum =0;
                for (var j = i; j > (i-n); j--) {
                    if (aData[j] != null && !isNaN(aData[j][1])) {
                        iNum++;
                        iStdDevSum += (Math.pow((aData[j][1]-iAvg),2));
                    }
                }
                if(iNum > 1) iNum = iNum -1; // 
                iStdDev = Math.sqrt(iStdDevSum/iNum);
                var ibBandUpper = iAvg + dev*iStdDev;
                var ibBandLower = iAvg - dev*iStdDev;
				if(isNaN(aData[i][1])){
					aCache[0].push([i,NaN,NaN,aData[i][aData[i].length-1]]);
               	 	aCache[1].push([i,NaN,NaN,aData[i][aData[i].length-1]]);
				}else{
					aCache[0].push([i,ibBandUpper,aData[i][1],aData[i][aData[i].length-1]]);
                	aCache[1].push([i,ibBandLower,aData[i][1],aData[i][aData[i].length-1]]);
				}
                
                if (aData[i-n+1] != null && !isNaN(aData[i-n+1][1])) {
                   iSum -= aData[i-n+1][1] ;
                   m--;
                }
            }                
        }
        return aCache;
    },
    
    calculateMACD:function(aData,ty,slow,fast,signal) {
        if (fast < 0 || aData == null || aData.length < slow) {
            return [[],[],[]];
        }
        var aCache = [[],[],[]];
        var slowEma = this.calculateEma(aData,ty,slow);
        var fastEma = this.calculateEma(aData,ty,fast);
        var lg = aData.length;
        var iMacd, divergence;
        for (var i = 0; i < lg; i++) {
            iMacd = fastEma[i][1] - slowEma[i][1];
            aCache[0][i] = [i,iMacd,aData[i][aData[i].length-1]];
        }
        var sigualEma = this.calculateEma(aCache[0],ty,signal);
        for (j = 0;j<lg;j++) { 
            divergence = aCache[0][j][1] - sigualEma[j][1];  
            aCache[2][j] = [j,divergence,aData[j][aData[j].length-1]];
        }
        aCache[1] = sigualEma;
        return aCache;
    },
    
    calculateRSI : function (aData,ty,n){
        if (n< 0 || aData == null || aData.length < n) {
           return null;
        }
        var aCache = [], lg = aData.length;
        var iGain = 0, iLoss = 0,m = 0, iTemp = NaN, iTempLoss = NaN;
        for (var i = 0; i < lg; i++) {
            if (i < n) {
                aCache[i] = [i,NaN,aData[i][aData[i].length-1]];
                if (i>0 && !isNaN(aData[i][1])) {
                    var dx = isNaN(aData[i-1][1])?0:(aData[i][1] - aData[i-1][1]); 
                    if (dx > 0) { 
                        iGain+=dx;
                    } else {
                        iLoss+=dx;
                    }
                    m++;
                }
            } else if (i == n) { 
                var dx = isNaN(aData[i-1][1])?0:(aData[i][1] - aData[i-1][1]); 
                if (dx > 0) { 
                   iGain += dx;
                } else {
                   iLoss+=dx;
                }
                m++;
                var iAvgGain = m>0?iGain/m:NaN, iAvgLoss = m>0?iLoss/m:NaN;
                var iRsi = 100-(100/(1+Math.abs(iAvgGain/iAvgLoss)));
                aCache[i] = [i,iRsi,aData[i][aData[i].length-1]];
                iTemp = iAvgGain; 
                iTempLoss = iAvgLoss;
            } else {
                var dx = aData[i][1] - aData[i-1][1]; 
				var dxLoss = !isNaN(dx)&& dx<0 ?dx:0, dxGain = dx>0&&!isNaN(dx)?dx:0; 
                var iAvgGain = (iTemp*(n-1)+dxGain)/n, iAvgLoss = (iTempLoss*(n-1)+dxLoss)/n;
                var iRsi = 100-(100/(1+Math.abs(iAvgGain/iAvgLoss)));
                aCache[i] = [i,iRsi,aData[i][aData[i].length-1]];
                iTemp = iAvgGain; 
                iTempLoss = iAvgLoss;
            }
          	if(isNaN(aData[i][1])){
				aCache[i] = [i,NaN,aData[i][aData[i].length-1]];
			}
        }
        return aCache;
    },
    
    _calculateStochastic: function(aData,ty,k,d,y) {
        if (k < 0 || aData == null || aData.length < k) { 
            return [[],[]];
        }
        var aCache = [[],[]]; 
//        if (ty == "i") {
//            return [[],[]];  // intraday data.
//        }
        var lg = aData.length;
        for (var i = 0; i< lg; i++) {
            if (i < (k - 1)) {
                aCache[0][i] = [i,NaN,aData[i][aData[i].length-1]];
            } else { 
                var iMax = 0; iMin = 1000000;
                for (j = i; j > i-k; j--) {
                    if (aData[j][2] > iMax) {
                        iMax = aData[j][2];
                    }
                    if (aData[j][3] < iMin) {
                        iMin = aData[j][3];
                    }
                }
                var iK = (iMax<=iMin?NaN:100*(aData[i][1] - iMin)/(iMax-iMin));
                aCache[0][i] = [i,iK,aData[i][aData[i].length-1]];
            }
          
        }
		var aSoure = aCache[0], lgg = aSoure.length, iSum = 0, m = 0;
            for (var h = 0;h < lgg; h++) {
	             if (h < (d-1)) {
	                 aCache[1][h] = [h,NaN,aSoure[h][2]]; 
	                 if (!isNaN(aSoure[h][1])) {
	                     iSum += aSoure[h][1];
	                     m++;
	                 } 
	             } else {
	                 if (!isNaN(aSoure[h][1])) {
	                     iSum+=aSoure[h][1];
	                     m++;
	                 } 
	                 aCache[1][h] = [h,(m>0?iSum/m:NaN),aSoure[h][2]];
	                 if (!isNaN(aSoure[h-d+1][1])) {
	                    iSum-=aSoure[h-d+1][1];
	                    m--;
	                 }
	             }
				 if(isNaN(aSoure[h][1])){
				 	aCache[1][h] = [h,NaN,aSoure[h][2]]; 
				 }
            }  
        return aCache;
    },
    
    calculateSlowStochastic:function(aData,ty,k,d) {
        return this._calculateStochastic(aData,ty,k,d,"slow");
    },
    
    calculateFastStochastic:function(aData,ty,k,d) {
        return this._calculateStochastic(aData,ty,k,d,"fast");
    },
    
    calculateROC:function (aData,ty,n) {
        if (n< 0 || aData == null || aData.length < n) {
            return null;
        }
        aCache = [];
        var lg = aData.length;
		var preClose,roc;
        for (var i = 0;i < lg; i++) { 
            if (i < n) {
                aCache.push([i,NaN,aData[i][aData[i].length-1]]);
            } else {
                preClose = (aData[i-n] == null?NaN:aData[i-n][1]); 
               	roc = NaN;
                if (!isNaN(preClose)) {
                    roc = 100*(aData[i][1] - preClose)/preClose;
                }
                aCache[i] = [i,roc,aData[i][aData[i].length-1]];
            }
        }
        return aCache;
    },
    
    calculateWillR:function(aData,ty,n) { 
        if (n < 0 || aData == null || aData.length < n) {
            return null;
        }
        var aCache = []; 
        var lg = aData.length;
        var iHigh = 0, iLow = 1000000,iR;
        for (var i = 0; i < lg; i++) { 
            if (i < n) {
                if (aData[i] != null) {
					if (!isNaN(aData[i][2]) && aData[i][2] > iHigh) { 
                        iHigh = aData[i][2];
                    }
                    if (!isNaN(aData[i][3]) && aData[i][3] < iLow) {
                        iLow = aData[i][3];
                    }
                    if (i == n-1) { 
                        iR = 100*(aData[i][1]-iHigh)/(iHigh-iLow); 
                        aCache[i] = [i,iR,aData[i][aData[i].length-1]]; 
                    } else {
                        aCache[i] = [i,NaN,aData[i][aData[i].length-1]];
                    }
                }
            } else {
                iHigh = 0, iLow = 1000000;
                for (var j = i; j >i - n; j--) {
                    if (!isNaN(aData[j][2]) && aData[j][2] > iHigh) {
                        iHigh = aData[j][2];
                    }
                    if (!isNaN(aData[j][3]) && aData[j][3] < iLow) {
                        iLow = aData[j][3];
                    }
                }
                iR = 100*(aData[i][1]-iHigh)/(iHigh-iLow);
				aCache[i] = [i,iR,aData[i][aData[i].length-1]];
            }         
        }
        return aCache;
    },
    
    calculatePSAR:function(aData,ty,a,maxA) {
        if (aData == null || aData.length < 2) {
            return;
        }
        var aCache = [];
        var lg = aData.length;
        var iTrend, iEp, iA = a, iSAR, iMax = 0, iMin = 1000000; 
        var preTrend = [,];
        aCache[0] = [0,NaN,NaN,aData[0][aData[0].length-1]];var flag = false;
        for (var i = 2; i <= lg; i++) { 
            if (i >= 2&&!flag) {
				if(isNaN(aData[i-1][2])||isNaN(aData[i-1][3])||isNaN(aData[i-1][1])){
					aCache[i-1] = [i-1,NaN,aData[i-1][1],aData[i][aData[i].length-1]]; 
					continue;
				}
				flag = true;
                var mm = aData[i-2][2]>aData[i-1][2]?aData[i-2][2]:aData[i-1][2]; 
                var nn = aData[i-2][3]<aData[i-1][3]?aData[i-2][3]:aData[i-1][3];
                if (aData[i][1] >= aData[i-1][1]) {     // up trend
                    iTrend = 0; iSAR  = nn; 
                    iEp = mm; aCache[i-1] = [i-1,iSAR,aData[i-1][1],aData[i][aData[i].length-1]]; 
                    preTrend[0] = preTrend[1] = 0;
                } else {                                       // down trend.
                    iTrend  = 1; iSAR =mm; 
                    iEp = nn; aCache[i-1] = [i-1,iSAR,aData[i-1][1],aData[i-1][aData[i-1].length]];
                    preTrend[0] = preTrend[1] = 0;
                } 
            }
            var iCurrentSAR = (isNaN(iSAR)||isNaN(iEp))?NaN:(iSAR + iA*(iEp-iSAR)); 
            if (i == lg) {
                aCache[i-1] = [i-1,iCurrentSAR,aData[1-1][1],aData[i-1][aData[i-1].length-1]];
				if(isNaN(aData[i-1][1])){
				 	aCache[i-1] = [i,NaN,NaN,aData[i-1][aData[i-1].length-1]];
				}
                break;
            }
            if (isNaN(aData[i][1]) || isNaN(aData[i][2]) || isNaN(aData[i][3])) {
                aCache[i-1] = [i,NaN,NaN,aData[i][aData[i].length-1]];
                continue;
            }
            if (preTrend[0] == preTrend[1]) {
                var min,max
                if (preTrend[1] == 0) {                                                                  //up trend
                    if (i>1 && !isNaN(aData[i-1][3]) && !isNaN(aData[i-2][3])) {         
                        min = (aData[i-1][3]<aData[i-2][3]?aData[i-1][3]:aData[i-2][3]); 
                        if (iCurrentSAR > min) { 
                            iCurrentSAR = min;
                        }
                    }
                } else {                                                                                      //down trend
                    if (i>1&& !isNaN(aData[i-1][2]) && !isNaN(aData[i-2][2])) {
                        max = (aData[i-1][2]>aData[i-2][2]?aData[i-1][2]:aData[i-2][2]);
                        if (iCurrentSAR < max) {
                            iCurrentSAR = max;
                        }
                    }
                }             
            } else {
                iCurrentSAR = iEp;
            }
            aCache[i-1] = [i-1,iCurrentSAR,aData[i-1][1],aData[i-1][aData[i-1].length-1]];
            if (preTrend[1] == 1) {
                if(aData[i][3] < iEp) { 
                    iEp = aData[i][3]; 
                    if (iA+0.02 <= maxA) iA = iA+0.02;
                 }
            } else {  
                if (aData[i][2] > iEp) {
                    iEp = aData[i][2];
                    if (iA+0.02 <= maxA) iA = iA+0.02;
                }
            }                  
            if ((preTrend[1]==0 && iCurrentSAR > aData[i][3]) || (preTrend[1] == 1 && iCurrentSAR < aData[i][2])) {
                iA = a;
                if (iTrend == 0) { 
                    iTrend = 1;
                } else {
                    iTrend  = 0;
                }
            } 
            iSAR = iCurrentSAR;
            preTrend [0] = preTrend[1];
            preTrend [1] = iTrend;
        }
        return aCache;
    },
    calculateDmi:function(aData,ty,n) {
        if (aData == null || !aData.length) {
            return [[], [], []];
        }
        var arr = [], dm = [], dm1 = dm2 = 0, p = 14, k = 2 / (p + 1), t1 = t2 = 0, tr = 0, s1 = s2 = s3 = 0, e1 = [], e2 = [], e3 = [], kp = 0, di1 = di2 = 0, dx = [], dxSum = 0, adx = [], tmax = 0;
        dm1[0] = dm2[0] = tr[0] = 0, dm[0] = 0;
        var a1 = [], a2 = [], a3 = [];
        a1[0] = a2[0] = a3[0] = [0, NaN, aData[0][aData[0].length - 1]];
        for (var i = 1; i < aData.length; i++) {
            a1[i] = [i, NaN, aData[i][aData[i].length - 1]];
            a2[i] = [i, NaN, aData[i][aData[i].length - 1]];
            a3[i] = [i, NaN, aData[i][aData[i].length - 1]];
            t1 = aData[i][2] - aData[i - 1][2];
            t2 = aData[i - 1][3] - aData[i][3];
            if (t1 > t2) { // up movement
                if (t1 > 0) 
                    dm1 = t1;
                else 
                    dm1 = 0;
                dm2 = 0
            } else if (t1 < t2) {// down movement.
                if (t2 > 0) 
                    dm2 = t2;
                else 
                    dm2 = 0;
                dm1 = 0;
            } else { // t1==t2
                dm1 = dm2 = 0;
            }
            tr = Math.max(Math.abs(aData[i][2] - aData[i][3]), Math.max(Math.abs(aData[i][2] - aData[i - 1][1]), Math.abs(aData[i][3] - aData[i - 1][1])));
            if (isNaN(dm1)) {
                dm1 = 0;
            }
            if (isNaN(dm2)) {
                dm2 = 0;
            }
            if (isNaN(tr)) {
                tr = 0;
            }
            
            if (i < p) {
                s1 += dm1;
                s2 += dm2;
                s3 += tr;
            }
            if (i == (p - 1)) {
                e1[i] = s1 / p;
                e2[i] = s2 / p;
                e3[i] = s3 / p;
                a1[i][1] = 100 * e1[i] / e3[i];
                a2[i][1] = 100 * e2[i] / e3[i];
            }
            //kp = aData[i][1]*k;
            if (i >= p) {
                e1[i] = dm1 * k + (1 - k) * e1[i - 1];
                e2[i] = dm2 * k + (1 - k) * e2[i - 1];
                e3[i] = tr * k + (1 - k) * e3[i - 1];
                di1 = e1[i] / e3[i];
                di2 = e2[i] / e3[i];
                dx[i] = 100 * Math.abs(di1 - di2) / (di1 + di2);
                a1[i][1] = di1 * 100;
                a2[i][1] = di2 * 100;
            }
            if (i >= p && i < 2 * p) {
                if (!isNaN(dx[i])) 
                    dxSum += dx[i];
            }
            if (i == (2 * p - 1)) {
                adx[i] = dxSum / p;
                a3[i][1] = adx[i];
            }
            if (i >= 2 * p && !isNaN(dx[i])) {
                adx[i] = dx[i] * k + (1 - k) * (isNaN(adx[i - 1]) ? 0 : adx[i - 1]);
                a3[i][1] = adx[i];
            }
			if(isNaN(aData[i][1])){
				a1[i][1]=NaN;
				a2[i][1]=NaN;
				a3[i][1]=NaN;
			}
        }
        return [a1, a2, a3];
	},
	/*
	  	Data point needed: high, low
		Parameter range: 2<n<250
		Default parameter: 2
		Get the maximum value within the scope of 20.
		Get the minimum value within the scope of 20.
		Then draw the two lines by maximum,minimum
		Hard
		Overlay
	 */
	calculatePriceChannel:function(aData,ty,n) {
		//if(ty=="i")return[[],[]];
		if (isNaN(n)||n<2||n>250||aData==null||aData.length<n) {
            return [[],[]];
        }
		var arr1=[],arr2=[],max=aData[0][2],min=aData[0][3],imax=imin=0;
		arr1.push([0,NaN,NaN,aData[0][aData[0].length - 1]]);
		arr2.push([0,NaN,NaN,aData[0][aData[0].length - 1]]);
		for(var i = 1;i<aData.length;i++){
			if(max<=aData[i][2]){
				max = aData[i][2];
				imax  =i;
			}
			if(min>=aData[i][3]){
				min = aData[i][3];
				imin = i;
			}
			if((i-n)==imax){
				max =0;
				var begin = imax +1,end = imax +n+1;
				for(var k = begin;k<end;k++){
					if(max <=aData[k][2]){
						max = aData[k][2];
						imax  =k;
					}
				}
			}
			if((i-n)==imin){
				var begin = imin +2,end = imin +n+1;
				min = aData[begin-1][3];
				imin = begin-1;
				for(var k = begin;k<end;k++){
					if(min>=aData[k][3]){
						min = aData[k][3];
						imin = k;
					}
				}
			}
			if(i>n-2){
				arr1.push([i,max,aData[i][1],aData[i][aData[i].length - 1]]);
				arr2.push([i,min,aData[i][1],aData[i][aData[i].length - 1]]);
			}else{
				arr1.push([i,NaN,NaN,aData[i][aData[i].length - 1]]);
				arr2.push([i,NaN,NaN,aData[i][aData[i].length - 1]]);
			}
			if(isNaN(aData[i][1])&&typeof(arr1[i])!="undefined"&&typeof(arr2[i])!="undefined"){ //intraday
				arr1[i]=[i,NaN,NaN,aData[i][aData[i].length - 1]];
				arr2[i]=[i,NaN,NaN,aData[i][aData[i].length - 1]];
			}
		}
		return [arr1,arr2];
	},
	/*
	 	Data point needed: MA
		Parameter range: 0<MA<250, 1%<n<10%
		Default parameter: MA=9, n=2%
		moving average, then get the data
		the two line are around the middle line
		Easy
		Overlay
	 */
	calculateMAEnvelope:function(aData,ty,n,per) {
		if (isNaN(n)||isNaN(per)||n<0||n>250||aData==null||aData.length<n) {
            return [[],[]];
        }
		var arr1=[],arr2=[],vol=[],sum=0,m=0,d=0,d1=0,d2=0,ad1=null,ad2=null;
		for(var i = 0;i<aData.length;i++){
			ad1 = aData[i][1];
			ad2 = aData[i][1];
			if(!isNaN(aData[i][1])){
				sum +=aData[i][1];
				m++;
			}
			if(i<n){
				if(i==(n-1)){
					vol =[i,sum/n,aData[i][aData[i].length - 1]];
					d = m>0?sum/m:NaN;
					d1 =d*(100+per)/100;
					d2 =d*(100-per)/100;
				}else{
					vol =[i,NaN,aData[i][aData[i].length - 1]];
					d1=d2=NaN;
				}
			}else{
				if(!isNaN(aData[i-n][1])){
					sum -=aData[i-n][1];
					m--;
				}
				d = m>0?sum/m:NaN;
				d1 =d*(100+per)/100;
				d2 =d*(100-per)/100;
			}
			if(isNaN(d1)){
				ad1=NaN;
			}
			if(isNaN(d2)){
				ad2=NaN;
			}
			if(ad1=="NaN"){
				ad1=NaN;
			}
			if(ad2=="NaN"){
				ad2=NaN;
			}
			//if(!isNaN(aData[i][1])){//intraday
				arr1.push([i,d1,ad1,aData[i][aData[i].length - 1]]);
				arr2.push([i,d2,ad2,aData[i][aData[i].length - 1]]);
			//}else{
				//arr1.push([i,NaN,NaN,aData[i][aData[i].length - 1]]);
				//arr2.push([i,NaN,NaN,aData[i][aData[i].length - 1]]);
			//}
		}
		return [arr1,arr2];
	},
	/*
		Data point needed: price
		Parameter range: 0<n<200
		Default parameter: 2
		MA
		Easy
		New
	 */
	calculateMomentum:function(aData,ty,n) {
        if (isNaN(n)||n<1||n>199 || aData == null || aData.length < n) {
            return [];
        }
        var arr=[];
		for(var i = 0;i<aData.length;i++){
			arr.push((i<n?[i,NaN,aData[i][aData[i].length - 1]]:[i,(aData[i][1]-aData[i-n][1]),aData[i][aData[i].length - 1]]));
		}
		return arr;
    }, 
	/*
	 	Data point needed: high, low, close
		Parameter range: no parameter
		bp=close-min(low,prev close);
		tr = max(high,prevclose)-min(low,prevclose);
		avg7=(bp1+...+bp7)/(tr1+....+tr7);
		ult = 100*(4*avg7+2*avg14+avg28)/(4+2+1);
		Intermediate
		New
	 */
	calculateUlt:function(aData,ty,n) {
        if (aData == null) {
            return [];
        }
        var arr=[],bp=[],tr=[],d=0,ult=[],k=0,bpSum=0,trSum=0,temp=0,result=0,tbp=0,ttr=0;
		for(var i = 0;i<aData.length;i++){
			d = aData[i];
			if(i==0){
				bp[i]=d[1]-d[3];
				tr[i]=d[2]-d[3];
			}else{
				tbp = d[1]-Math.min(d[3],aData[i-1][1]);
				ttr =Math.max(d[2],aData[i-1][1])-Math.min(d[3],aData[i-1][1]);
				if(isNaN(tbp)){
					bp[i] =bp[i-1];
				}else{
					bp[i]=tbp;
				}
				if(isNaN(ttr)){
					tr[i] =tr[i-1];
				}else{
					tr[i]=ttr;
				}
			}
			if(isNaN(bp[i])){
				bp[i]=0;
			}
			if(isNaN(tr[i])){
				tr[i]=0
			}
			if(i<28){
				bpSum +=bp[i];
				trSum +=tr[i];
				if(i==(k+6)){
					ult[k]=[];
					ult[k][0]=[bpSum,trSum];
				}
				if(i==(k+13)){
					ult[k][1]=[bpSum,trSum];
				}
				if(i==(k+27)){
					ult[k][2]=[bpSum,trSum];
				}
			}
			if(i>=27){
				if(k>0){
					temp = ult[k-1];
					ult[k]=[];
					ult[k][0]=[temp[0][0]-bp[i-28]+bp[i-21],temp[0][1]-tr[i-28]+tr[i-21]];
					ult[k][1]=[temp[1][0]-bp[i-28]+bp[i-14],temp[1][1]-tr[i-28]+tr[i-14]];	
					ult[k][2]=[temp[2][0]-bp[i-28]+bp[i],temp[2][1]-tr[i-28]+tr[i]];
				}
				result = 100*(4*ult[k][0][0]/ult[k][0][1]+2*ult[k][1][0]/ult[k][1][1]+ult[k][2][0]/ult[k][2][1])/7;
				arr.push([i,result,aData[i][aData[i].length - 1]]);
				k++;
			}else{
				arr.push([i,NaN,aData[i][aData[i].length - 1]]);
			}
			if(isNaN(aData[i][1])&&typeof(arr[i][1])!="undefined"){ //intraday
				arr[i][1]=NaN;
			}
		}
		return arr;
    },
	/*
	 	Data point needed: close, high, low, volume
		Parameter range: 0<m<100
		Default parameter: m=14
		tp = (high+low+close)/3;
		mf = tp*vol;
		mr = positive fm/negative mf;
		mfi = 100-(100/(1+mr));
		Intermediate
		New
	 */
	calculateMFI:function(aData,volData,ty,n){
		if(!volData) return [];
		if(isNaN(n)||n<1||n>99){return [];}
		var len = aData.length;
		var typicalp=[],pn=[],pmf=0,nmf=0,arr=[],temp;
		for(var i = 0;i<len;i++){
			if(isNaN(volData[i][1])){
				volData[i][1] =0;
			}
			temp =[i,NaN,aData[i][aData[i].length - 1]];
			if (aData[i][1] == 0) { //no close price
				pn[i] = 0;
			} else {
				typicalp[i] = (aData[i][1] + aData[i][2] + aData[i][3]) / 3;
				if (i > 0) {
					if(isNaN(typicalp[i])){
						pn[i - 1] = 0;
					}else{
						pn[i - 1] = typicalp[i] > typicalp[i - 1] ? 1 : (typicalp[i] == typicalp[i - 1] ? 0 : -1);
					}
					if (pn[i - 1] > 0) {
						pmf += typicalp[i] * volData[i][1];
					} else if (pn[i - 1] < 0) {
						nmf += typicalp[i] * volData[i][1];
					}
					if (i >= n) {
						if (i>n) {
							if (pn[i - n - 1] > 0) {
								pmf -= typicalp[i - n] * volData[i - n][1]
							} else if (pn[i - n - 1] < 0) {
								nmf -= typicalp[i - n] * volData[i - n][1];
							}
						}
						if (!isNaN(aData[i][1])) { //intraday
							temp[1] = 100 * pmf / (pmf + nmf);
						}
					}
				}
			}
			arr.push(temp);
		}
		return arr;
	},
	/*
	 	Data point needed: volume, close price, high, low
		Parameter range:  no parameter, daily
		Default parameter: no parameter (however, we can draw MA OBV line, MA=30)
		Volume x [Close - (High + Low)/2]
		Easy
		New
	 */
	calculateVolumeAcc:function(aData,volData,ty,n){
		 // ty>10 volume data ty=1 historical daily data; ty=2 historical weekily data; ty=3 historical monthly data; ty=0 intraydayData.
		var len = aData.length;
		var vacc=[],arr=[];
		if(!volData) return arr;
		for(var i = 0;i<len;i++){
			if(isNaN(volData[i][1])){
				volData[i][1] =0;
			}
			if (aData[i][1] == undefined || aData[i][1] == null || isNaN(aData[i][1]) || aData[i][1] == 0) { //no close price
				vacc[i] = NaN;
			} else {
				vacc[i] = volData[i][1] * (aData[i][1] - (aData[i][2] + aData[i][3]) / 2);
			}
			arr.push([i,vacc[i],aData[i][aData[i].length-1]]);			
		}
		return arr;
	},	
	/*
		Data point needed: Volume, Close, Yesterday close
		Parameter range: no parameter, daily
		if(cl>cl1) + volume
			 ==    0
			 <     -
		obv = obvPre + ?;
		Easy
		New
	 */
	calculateObv:function(aData,volData,ty,n){
		var len = aData.length;
		var obv=[],arr=[];
		for(var i = 0;i<len;i++){
			if(isNaN(volData[i][1])){
				volData[i][1] =0;
			}
			if(i==0){
				obv[i]=volData[0][1];
			}else{
				if (aData[i][1] == undefined || aData[i][1] == null || isNaN(aData[i][1]) || aData[i][1] == 0) { //no close price
					obv[i] = NaN;
				}else if(i>0&&isNaN(aData[i-1][1])){ //jacky 2010.3.19 
					obv[i] = volData[i][1];
				} else{
					if(aData[i][1]>aData[i-1][1]){
						obv[i] = obv[i-1]+volData[i][1];
					}else if(aData[i][1]==aData[i-1][1]){
						obv[i] =obv[i-1];
					}else if(aData[i][1]<aData[i-1][1]){
						obv[i] =obv[i-1]-volData[i][1];
					}
				}
			}
			arr.push([i,obv[i],aData[i][aData[i].length-1]]);			
		}
		return arr;
	},
	/*
	 	Methodology: Volume Ratio = Up Volume / Down Volume
		UPVOL = SUM(VOL, m)  for up bars (CL > CL1)
		DOWNVOL = SUM(VOL, m)  for down bars (CL < CL1)
		UDVR = MA(UPVOL / DOWNVOL, n, maType)
		m = Number of Periods
		n = Smoothing Period
		Referral Link:
		1.http://www.linnsoft.com/tour/techind/udvr.htm
		Data point needed: volume, close price, yesterday price
		Parameter range: 0<m<100, 0<n<100
		Default parameter: m=7, n=5
		Hard
		New
	 */
	calculateUpDownRatio:function(aData,volData,ty,m,n){
		if(isNaN(n)||n<1||n>99||isNaN(m)||m<1||m>99){
			return [];
		}
		var len =aData.length;
		var arr=[],close=[],sup=sdown=0,ud=[];
		for (var i = 1, j = 0; i < len; i++, j++) {
			if(isNaN(volData[i][1])){
				volData[i][1] =0;
			}
			if (aData[i][1] == undefined || aData[i][1] == null || isNaN(aData[i][1]) || aData[i][1] == 0) {
				close[i - 1] = 0;
			} else if (aData[i][1] > aData[i - 1][1]) {
				close[i - 1] = 1;
			} else if (aData[i][1] < aData[i - 1][1]) {
				close[i - 1] = -1;
			}else if (aData[i][1] == aData[i - 1][1]) {
				close[i - 1] = 2;
			}else {
				close[i - 1] = 0;
			}
			if (close[j] == 1) {
				sup += volData[j + 1][1];
			} else if (close[j] == -1) {
				sdown += volData[j + 1][1];
			}else if (close[j] == 2) {
				sdown += volData[j + 1][1]/2;
				sup += volData[j + 1][1]/2;
			}
			if (j < m - 1) {
				ud[j] = -1;
				continue;
			}
			if (j >= m) {
				if (close[j - m] == 1) {
					sup -= volData[j - m + 1][1];
				} else if (close[j - m] == -1) {
					sdown -= volData[j - m + 1][1];
				}else if (close[j - m] == 2) {
					sdown -= volData[j - m + 1][1]/2;
					sup -= volData[j - m + 1][1]/2;
				}
			}
			ud[j] = [sup, sdown];
		}
		var mnlen = m+n,sum = 0,harr=[];
		arr.push([0,NaN,aData[0][aData[0].length-1]]);
		for(var i = 0;i<ud.length;i++){
			if(ud[i]==-1){
				arr.push([i+1,NaN,aData[i][aData[i].length-1]]);
				continue;
			}
			if(ud[i][1]<0.00000001){
				harr[i]=0;
			}else{
				harr[i]=ud[i][0]/ud[i][1];
			}
			sum +=harr[i];
			if(i<mnlen-2){
				arr.push([i+1,NaN,aData[i][aData[i].length-1]]);
			}else{
				if(i>mnlen-2){
					sum -=harr[i-n];
				}
				if(isNaN(aData[i][1])){ //intraday
					arr.push([i+1,NaN,aData[i+1][aData[i+1].length-1]]);
				}else{
					arr.push([i+1,sum/5,aData[i+1][aData[i+1].length-1]]);
				}
			}
		}
		return arr;
	},
	/*
	  	Data point needed: close price, volume
		Parameter range: daily, Volume MA 2<n<200
		Default parameter: 50
		Easy
	 */
	calculateVolPlus:function(aData,volData,ty,n) {
		if (isNaN(n)||volData==null||volData.length<n) {
            return [];
        }
		var arr=[],vol=[],sum=0;
		for(var i = 0;i<volData.length;i++){
			if(isNaN(volData[i][1])){
				volData[i][1] =0;
			}
			sum +=volData[i][1];
			if(i<n){
				if(i==(n-1)){
					vol =[i,sum/n,aData[i][aData[i].length - 1]];
				}else{
					vol =[i,NaN,aData[i][aData[i].length - 1]];
				}
			}else{
				sum -=volData[i-n][1];
				vol =[i,sum/n,aData[i][aData[i].length - 1]];
			}
			arr.push(vol);
		}
		return arr;
	},
	/*
	 	Data point needed: Volume, accumulate volume, price
		Parameter range: no parameter
		This is a horizontal histogram that overlays a price chart. The histogram bars stretch from left
		  to right starting at the left side of the chart. The length of each bar is determined by the 
		  cumulative total of all volume bars for the periods during which the closing price fell within 
		  the vertical range of the histogram bar.
		Hard
		Overlay
	 */
	calculateVolByPrice:function(aData,volData,ty,n){
		if (aData==null||volData==null) {
            return [];
        }
		var len = aData.length,vacc=[],arr=[],min = Number.MAX_VALUE, max=Number.MIN_VALUE ;
		for(var i = 0;i<len ;i++){
			if(!isNaN(aData[i][1])){
				max = Math.max(max,aData[i][1]);
				min = Math.min(min,aData[i][1]);
			}
						
		}
		var avg = (max-min)/14;
		for(var i = 0;i<14;i++){
			if(i==0){
				arr.push([min,(min+avg),0])
			}else{
				arr.push([arr[i-1][1],(arr[i-1][1]+avg),0])
			}
		}
		var d=0;
		for(var i = 0;i<aData.length;i++){
			if(isNaN(volData[i][1])){
				volData[i][1] =0;
			}
			d = aData[i][1];
			if(d<arr[0][1]){
				arr[0][2] +=volData[i][1];
			}else if(d<arr[1][1]){
				arr[1][2] +=volData[i][1];
			}else if(d<arr[2][1]){
				arr[2][2] +=volData[i][1];
			}else if(d<arr[3][1]){
				arr[3][2] +=volData[i][1];
			}else if(d<arr[4][1]){
				arr[4][2] +=volData[i][1];
			}else if(d<arr[5][1]){
				arr[5][2] +=volData[i][1];
			}else if(d<arr[6][1]){
				arr[6][2] +=volData[i][1];
			}else if(d<arr[7][1]){
				arr[7][2] +=volData[i][1];
			}else if(d<arr[8][1]){
				arr[8][2] +=volData[i][1];
			}else if(d<arr[9][1]){
				arr[9][2] +=volData[i][1];
			}else if(d<arr[10][1]){
				arr[10][2] +=volData[i][1];
			}else if(d<arr[11][1]){
				arr[11][2] +=volData[i][1];
			}else if(d<arr[12][1]){
				arr[12][2] +=volData[i][1];
			}else if(d<arr[13][1]){
				arr[13][2] +=volData[i][1];
			}
		}
		var large = arr[0][2];
		for(var i = 1;i<14;i++){
			large = Math.max(large,arr[i][2])
		}
		var v = [arr,[min,max],large];
		return v;
	},  
	calculateDividendYield:function(aData,dData,ty){
		var dlen = dData.length,alen = aData.length, arr=[],map={},mapNum = {},tempYear=0, dt;
		for(var i = 0;i<dlen;i++){
			if(dData[i].Type != 1) continue;
			dt = RTQ_ChartUtil.toDateFromIndex(dData[i].DateIndex);
			tempYear = dt.getFullYear();
			map[tempYear+""] = parseFloat(dData[i].Desc);
			if (!mapNum[tempYear + ""]) {
				mapNum[tempYear + ""] = 1;
			}else {
				mapNum[tempYear + ""]++;
			}
		}
		var year =0,val, isIntraday = !isNaN(ty);
		if(isIntraday){    // intraday 
			val = 100*map[tempYear+""]*mapNum[tempYear+""];
		}
		for(var i = 0;i<alen;i++){
			var tarr =[i,NaN,aData[i][aData[i].length - 1]];
			if(!isNaN(aData[i][1])){
				if(!isIntraday){
					year = RTQ_ChartUtil.toDateFromIndex(aData[i][aData[i].length-1]).getFullYear();
					val = 100*map[year+""]*mapNum[year+""];	
				}	
				tarr[1] = val/aData[i][1];
			}
			arr.push(tarr);
		}
		return arr;
	},
	calculateFVolatility:function(aData,ty){
		if(!aData || !aData.length)return [];
		var tr,low,high,sum = 0, fv = [], k = 2/(21+1);
		for(var i = 0;i < aData.length;i++){
			fv[i]=[aData[i][0],NaN,aData[i][aData[i].length-1]];
			if(aData[i].length<4||isNaN(aData[i][1])||isNaN(aData[i][2])||isNaN(aData[i][3]))continue;
			low = aData[i][3], high = aData[i][2];	
			if(i>0){
				if(aData[i-1][1]<aData[i][3]){
					low = aData[i-1][1];
				}
				if(aData[i-1][1]>aData[i][2]){
					high = aData[i-1][1];
				}
			}
			tr = (high-low)/low;
			if(i<21){
				sum+=tr;
			}else if (i==21){
				fv[i][1]=sum/21;
			}else{
				fv[i][1] = k*fv[i-1][1]+(1-k)*tr;
			}
		}
		return fv;
	},
	calculateVolatility:function(aData,ty,p,pa){
		if (!aData || aData.length==0) 
            return [];
		var arr = [];
        var arrHL = [],temp=0;
        for (var i = 0; i < aData.length; i++) {
            arrHL.push(aData[i][2] - aData[i][3]);
        }
        var arrEma1 = this.EMA(arrHL, p);
		for(var i = 0;i<aData.length;i++){
			if(i<(p+pa-1)){
				arr.push([i, NaN, aData[i][aData[i].length - 1]]); 
			}else{
				temp = (arrEma1[i]/arrEma1[i-pa]-1)*100;
				arr.push([i, temp, aData[i][aData[i].length - 1]]);
			}
		}
		return arr;
	},
	calculateMass:function(aData,ty){
        if (!aData || !aData.length) 
            return [];
        var arr = [], previous = NaN, temp = 0;
        var arrHL = [];
        for (var i = 0; i < aData.length; i++) {
            arrHL.push(aData[i][2] - aData[i][3]);
        }
        var arrEma1 = this.EMA(arrHL, 9);
        var arrEma2 = this.EMA(arrEma1, 9);
        var sum = 0,arrCompare=[];
        for (var i = 0; i < arrEma2.length; i++) {
            if (i < 16) {
                arr.push([i, NaN, aData[i][aData[i].length - 1]]);
                continue;
            }
			arrCompare[i]=arrEma1[i]/arrEma2[i];
            if (!isNaN(arrCompare[i])) {
                sum += arrCompare[i];
            }
            if (i < 40) {
				arr.push([i, NaN, aData[i][aData[i].length - 1]]);
                continue;
            }
            if (i > 40) {
                if (!isNaN(arrCompare[i - 25])) {
                    sum -= arrCompare[i - 25];
                }
            }
            arr.push([i, sum, aData[i][aData[i].length - 1]]);
        }
        return arr;
	},
	calculateAccDis:function(aData,volData,ty){
		if(!aData || !aData.length||volData.lenght==0)return [];
		var arr =[],previous = NaN,temp=0;
		for(var i = 0;i<aData.length;i++){
			temp = (2*aData[i][1]-aData[i][2]-aData[i][3])*volData[i][1]/(aData[i][2]-aData[i][3])+(isNaN(previous)?0:previous);
			arr[i]=[i,temp,aData[i][aData[i].length - 1]];
			if(!isNaN(temp)){
				previous = temp;
			}
		}
		return arr;
	},
	calculateWMA:function(aData,ty,p){
		if(!aData || !aData.length)return [];
		if(typeof(p)=="undefined"||p<1){
			p=15;
		}
		var denominator  = p*(p+1)/2,arr=[],tsum =0,index=0,sum;
		for(var i = 0;i<aData.length;i++){
			tsum =0;
			if(i<p-1||isNaN(aData[i][1])){
				arr[i]=[i,NaN,aData[i][aData[i].length - 1]];
			}else {
				sum  = denominator;
				index = 1;
				for (var j = i - p + 1; j <= i; j++,index++) {
					if(isNaN(aData[j][1])){ // filter the NaN data point.
						sum -= index;
					}else{
						tsum += aData[j][1]* index;
					}	
				}
				arr[i] = [i, tsum / sum, aData[i][aData[i].length - 1]];
			}
		}
		return arr;
	},
	calculateKeltner:function(aData,ty,e,t,a){
		if(!aData || !aData.length)return [];
		var arr=[],atr=[],ema=[],close=[],aa=[],bb=[],cc=[];
		for(var i =0;i<aData.length;i++){
			close.push(aData[i][1]);
		}
		atr = this.calculateAtr(aData,0,a);
		ema = this.EMA(close,e);
		for(var i =0;i<aData.length;i++){
			if(isNaN(ema[i])||isNaN(atr[i][1])){
				aa.push([i, NaN,NaN, aData[i][aData[i].length - 1]]);
				bb.push([i, NaN,NaN, aData[i][aData[i].length - 1]]);
				cc.push([i, NaN,NaN, aData[i][aData[i].length - 1]]);
			}else{
				aa.push([i, ema[i]+2*atr[i][1],aData[i][1], aData[i][aData[i].length - 1]]);
				bb.push([i, ema[i],aData[i][1], aData[i][aData[i].length - 1]]);
				cc.push([i, ema[i]-2*atr[i][1],aData[i][1], aData[i][aData[i].length - 1]]);
			}
		}
		return [aa,bb,cc];
	},
	calculateAtr:function(aData,ty,p){
		if(!aData || !aData.length)return [];
			if(typeof(p)=="undefined"){
				p=14;
			}
			var arr = [],j=0,sum=0,hl=0,a=b=c=0,pre=0;
			for(var i = 0;i<aData.length;i++){
				
				a = aData[i][2]-aData[i][3];
				if(i>0){
					b = Math.abs(aData[i][2]-aData[i-1][1]);
					c = Math.abs(aData[i][3]-aData[i-1][1]);
				}
				if(i<p-1){
					if(i==0&&!isNaN(a)){
						j++;
						sum +=a;
					}else if(!isNaN(a)&&!isNaN(b)&&!isNaN(c)){
						j++;
						sum +=Math.max(Math.max(a,b),c);
					}
					arr.push([i, NaN, aData[i][aData[i].length - 1]]);
				}else if(i==p-1){
					if(!isNaN(a)&&!isNaN(b)&&!isNaN(c)){
						j++;
						sum +=Math.max(Math.max(a,b),c);
					}
					pre = sum/j;
					arr.push([i, pre, aData[i][aData[i].length - 1]]);
				}else{
					if(!isNaN(a)&&!isNaN(b)&&!isNaN(c)){
						pre = (pre*(p-1)+Math.max(Math.max(a,b),c))/p;
						arr.push([i, pre, aData[i][aData[i].length - 1]]);
					}else{
						arr.push([i, NaN, aData[i][aData[i].length - 1]]);
					}
				}
			}
			return arr;
	},
	calculateForceIndex:function(aData,volData,ty,p){
		if(!aData || !aData.length||volData.lenght==0)return [];
		var arr=[],ema=[];
		//arr.push([0, NaN, aData[0][aData[0].length - 1]]); 
		
		for(var i = 0;i<aData.length;i++){
			if(isNaN(aData[i][1])||isNaN(volData[i][1])||i==0){
				ema.push(NaN); 
			}else{
				ema.push((aData[i][1]-aData[i-1][1])*volData[i][1]); 
			}
		}
		ema = this.EMA(ema,p);
		for(var i =0;i<ema.length;i++){
			arr.push([i, ema[i], aData[i][aData[i].length - 1]]); 
		}
		return arr;
	}
};
var techIndicatorCal = new indicatorCalculation();
